﻿namespace Fiddler.WebFormats
{
    using System;
    using System.Collections;
    using System.Globalization;
    using System.Runtime.CompilerServices;
    using System.Runtime.InteropServices;
    using System.Text;

    public class JSON
    {
        private const int BUILDER_DEFAULT_CAPACITY = 0x800;

        private static void EatWhitespace(char[] json, ref int index)
        {
            while (index < json.Length)
            {
                if (" \t\n\r".IndexOf(json[index]) == -1)
                {
                    return;
                }
                index++;
            }
        }

        private static int GetLastIndexOfNumber(char[] json, int index)
        {
            int num = index;
            while (num < json.Length)
            {
                if ("0123456789+-.eE".IndexOf(json[num]) == -1)
                {
                    break;
                }
                num++;
            }
            return (num - 1);
        }

        private static bool IsNumeric(object o)
        {
            try
            {
                double.Parse(o.ToString());
            }
            catch (Exception)
            {
                return false;
            }
            return true;
        }

        private static bool isValidIdentifierChar(char c)
        {
            return ((((c == '-') || (c == '_')) || (c == '$')) || char.IsLetterOrDigit(c));
        }

        private static bool isValidIdentifierStart(char c)
        {
            return (((c == '_') || (c == '$')) || char.IsLetter(c));
        }

        public static object JsonDecode(string sJSON, out JSONParseErrors oErrors)
        {
            JSONParseErrors errors = new JSONParseErrors();
            errors.iErrorIndex = -1;
            errors.sWarningText = string.Empty;
            oErrors = errors;
            if (!string.IsNullOrEmpty(sJSON))
            {
                char[] json = sJSON.ToCharArray();
                int index = 0;
                bool success = true;
                return ParseValue(json, ref index, ref success, ref oErrors);
            }
            return null;
        }

        public static string JsonEncode(object json)
        {
            StringBuilder builder = new StringBuilder(0x800);
            if (!SerializeValue(json, builder))
            {
                return null;
            }
            return builder.ToString();
        }

        private static JSONTokens LookAhead(char[] json, int index)
        {
            int num = index;
            return NextToken(json, ref num);
        }

        private static JSONTokens NextToken(char[] json, ref int index)
        {
            EatWhitespace(json, ref index);
            if (index != json.Length)
            {
                char ch = json[index];
                index++;
                switch (ch)
                {
                    case '"':
                        return JSONTokens.STRING;

                    case ',':
                        return JSONTokens.COMMA;

                    case '-':
                    case '0':
                    case '1':
                    case '2':
                    case '3':
                    case '4':
                    case '5':
                    case '6':
                    case '7':
                    case '8':
                    case '9':
                        return JSONTokens.NUMBER;

                    case ':':
                        return JSONTokens.COLON;

                    case '[':
                        return JSONTokens.SQUARED_OPEN;

                    case ']':
                        return JSONTokens.SQUARED_CLOSE;

                    case '{':
                        return JSONTokens.CURLY_OPEN;

                    case '}':
                        return JSONTokens.CURLY_CLOSE;
                }
                index--;
                int num = json.Length - index;
                if ((((num >= 5) && (json[index] == 'f')) && ((json[index + 1] == 'a') && (json[index + 2] == 'l'))) && ((json[index + 3] == 's') && (json[index + 4] == 'e')))
                {
                    index += 5;
                    return JSONTokens.FALSE;
                }
                if ((((num >= 4) && (json[index] == 't')) && ((json[index + 1] == 'r') && (json[index + 2] == 'u'))) && (json[index + 3] == 'e'))
                {
                    index += 4;
                    return JSONTokens.TRUE;
                }
                if ((((num >= 4) && (json[index] == 'n')) && ((json[index + 1] == 'u') && (json[index + 2] == 'l'))) && (json[index + 3] == 'l'))
                {
                    index += 4;
                    return JSONTokens.NULL;
                }
                if (isValidIdentifierStart(json[index]))
                {
                    return JSONTokens.IMPLIED_IDENTIFIER_NAME;
                }
            }
            return JSONTokens.NONE;
        }

        private static ArrayList ParseArray(char[] json, ref int index, ref JSONParseErrors oErrors)
        {
            ArrayList list = new ArrayList();
            NextToken(json, ref index);
            bool flag = false;
            while (!flag)
            {
                JSONTokens tokens = LookAhead(json, index);
                if (tokens == JSONTokens.NONE)
                {
                    return null;
                }
                if (tokens == JSONTokens.COMMA)
                {
                    NextToken(json, ref index);
                }
                else
                {
                    if (tokens == JSONTokens.SQUARED_CLOSE)
                    {
                        NextToken(json, ref index);
                        return list;
                    }
                    bool success = true;
                    object obj2 = ParseValue(json, ref index, ref success, ref oErrors);
                    if (!success)
                    {
                        return null;
                    }
                    list.Add(obj2);
                }
            }
            return list;
        }

        private static double ParseNumber(char[] json, ref int index)
        {
            EatWhitespace(json, ref index);
            int lastIndexOfNumber = GetLastIndexOfNumber(json, index);
            int length = (lastIndexOfNumber - index) + 1;
            string s = new string(json, index, length);
            index = lastIndexOfNumber + 1;
            return double.Parse(s, CultureInfo.InvariantCulture);
        }

        private static Hashtable ParseObject(char[] json, ref int index, ref JSONParseErrors oErrors)
        {
            Hashtable hashtable = new Hashtable();
            NextToken(json, ref index);
            bool flag = false;
            while (!flag)
            {
                JSONTokens tokens = LookAhead(json, index);
                switch (tokens)
                {
                    case JSONTokens.NONE:
                        return null;

                    case JSONTokens.COMMA:
                        {
                            NextToken(json, ref index);
                            continue;
                        }
                    case JSONTokens.CURLY_CLOSE:
                        NextToken(json, ref index);
                        return hashtable;
                }
                string str = null;
                if (tokens == JSONTokens.IMPLIED_IDENTIFIER_NAME)
                {
                    str = ParseUnquotedIdentifier(json, ref index, ref oErrors);
                    if (str == null)
                    {
                        return null;
                    }
                }
                else
                {
                    str = ParseString(json, ref index);
                    if (str == null)
                    {
                        return null;
                    }
                }
                if (NextToken(json, ref index) != JSONTokens.COLON)
                {
                    return null;
                }
                bool success = true;
                object obj2 = ParseValue(json, ref index, ref success, ref oErrors);
                if (!success)
                {
                    oErrors.iErrorIndex = index;
                    return null;
                }
                hashtable[str] = obj2;
            }
            return hashtable;
        }

        private static string ParseString(char[] json, ref int index)
        {
            StringBuilder builder = new StringBuilder(0x800);
            EatWhitespace(json, ref index);
            index++;
            bool flag = false;
            while (!flag)
            {
                if (index == json.Length)
                {
                    break;
                }
                char ch = json[index++];
                if (ch == '"')
                {
                    flag = true;
                    break;
                }
                if (ch == '\\')
                {
                    if (index == json.Length)
                    {
                        break;
                    }
                    ch = json[index++];
                    if (ch == '"')
                    {
                        builder.Append('"');
                    }
                    else
                    {
                        if (ch == '\\')
                        {
                            builder.Append('\\');
                            continue;
                        }
                        if (ch == '/')
                        {
                            builder.Append('/');
                            continue;
                        }
                        if (ch == 'b')
                        {
                            builder.Append('\b');
                            continue;
                        }
                        if (ch == 'f')
                        {
                            builder.Append('\f');
                            continue;
                        }
                        if (ch == 'n')
                        {
                            builder.Append('\n');
                            continue;
                        }
                        if (ch == 'r')
                        {
                            builder.Append('\r');
                            continue;
                        }
                        if (ch == 't')
                        {
                            builder.Append('\t');
                        }
                        else if (ch == 'u')
                        {
                            string str;
                            int num = json.Length - index;
                            if (num < 4)
                            {
                                break;
                            }
                            uint num2 = uint.Parse(new string(json, index, 4), NumberStyles.HexNumber);
                            try
                            {
                                str = char.ConvertFromUtf32((int)num2);
                            }
                            catch (Exception)
                            {
                                str = "�";
                            }
                            builder.Append(str);
                            index += 4;
                        }
                    }
                }
                else
                {
                    builder.Append(ch);
                }
            }
            if (!flag)
            {
                return null;
            }
            return builder.ToString();
        }

        private static string ParseUnquotedIdentifier(char[] json, ref int index, ref JSONParseErrors oErrors)
        {
            EatWhitespace(json, ref index);
            StringBuilder builder = new StringBuilder(0x800);
            bool flag = false;
            while (!flag)
            {
                if (index == json.Length)
                {
                    break;
                }
                char c = json[index];
                if (!isValidIdentifierChar(c))
                {
                    if (builder.Length < 1)
                    {
                        return null;
                    }
                    flag = true;
                    break;
                }
                builder.Append(c);
                index++;
            }
            if (!flag)
            {
                return null;
            }
            oErrors.sWarningText = string.Format("{0}Illegal/Unquoted identifier '{1}' at position {2}.\n", oErrors.sWarningText, builder.ToString(), (int)index);
            return builder.ToString();
        }

        private static object ParseValue(char[] json, ref int index, ref bool success, ref JSONParseErrors oErrors)
        {
            switch (LookAhead(json, index))
            {
                case JSONTokens.CURLY_OPEN:
                    return ParseObject(json, ref index, ref oErrors);

                case JSONTokens.SQUARED_OPEN:
                    return ParseArray(json, ref index, ref oErrors);

                case JSONTokens.STRING:
                    return ParseString(json, ref index);

                case JSONTokens.NUMBER:
                    return ParseNumber(json, ref index);

                case JSONTokens.TRUE:
                    NextToken(json, ref index);
                    return true;

                case JSONTokens.FALSE:
                    NextToken(json, ref index);
                    return false;

                case JSONTokens.NULL:
                    NextToken(json, ref index);
                    return null;

                case JSONTokens.IMPLIED_IDENTIFIER_NAME:
                    return ParseUnquotedIdentifier(json, ref index, ref oErrors);
            }
            success = false;
            return null;
        }

        private static bool SerializeArray(IList anArray, StringBuilder builder)
        {
            builder.Append("[");
            bool flag = true;
            for (int i = 0; i < anArray.Count; i++)
            {
                object obj2 = anArray[i];
                if (!flag)
                {
                    builder.Append(", ");
                }
                if (!SerializeValue(obj2, builder))
                {
                    return false;
                }
                flag = false;
            }
            builder.Append("]");
            return true;
        }

        private static void SerializeNumber(double number, StringBuilder builder)
        {
            builder.Append(Convert.ToString(number, CultureInfo.InvariantCulture));
        }

        private static bool SerializeObject(IDictionary anObject, StringBuilder builder)
        {
            builder.Append("{");
            IDictionaryEnumerator enumerator = anObject.GetEnumerator();
            for (bool flag = true; enumerator.MoveNext(); flag = false)
            {
                string aString = enumerator.Key.ToString();
                object obj2 = enumerator.Value;
                if (!flag)
                {
                    builder.Append(", ");
                }
                SerializeString(aString, builder);
                builder.Append(":");
                if (!SerializeValue(obj2, builder))
                {
                    return false;
                }
            }
            builder.Append("}");
            return true;
        }

        private static bool SerializeObjectOrArray(object objectOrArray, StringBuilder builder)
        {
            if (objectOrArray is IDictionary)
            {
                return SerializeObject((IDictionary)objectOrArray, builder);
            }
            return ((objectOrArray is IList) && SerializeArray((IList)objectOrArray, builder));
        }

        private static void SerializeString(string aString, StringBuilder builder)
        {
            builder.Append("\"");
            char[] chArray = aString.ToCharArray();
            for (int i = 0; i < chArray.Length; i++)
            {
                switch (chArray[i])
                {
                    case '\b':
                        {
                            builder.Append(@"\b");
                            continue;
                        }
                    case '\t':
                        {
                            builder.Append(@"\t");
                            continue;
                        }
                    case '\n':
                        {
                            builder.Append(@"\n");
                            continue;
                        }
                    case '\f':
                        {
                            builder.Append(@"\f");
                            continue;
                        }
                    case '\r':
                        {
                            builder.Append(@"\r");
                            continue;
                        }
                    case '"':
                        {
                            builder.Append("\\\"");
                            continue;
                        }
                    case '\\':
                        {
                            builder.Append(@"\\");
                            continue;
                        }
                }
                char ch = chArray[i];
                int num2 = Convert.ToInt32(ch);
                if ((num2 >= 0x20) && (num2 <= 0x7e))
                {
                    builder.Append(ch);
                }
                else
                {
                    builder.Append(@"\u" + num2.ToString("x").PadLeft(4, '0'));
                }
            }
            builder.Append("\"");
        }

        private static bool SerializeValue(object value, StringBuilder builder)
        {
            if (value == null)
            {
                builder.Append("null");
            }
            else if (value is string)
            {
                SerializeString((string)value, builder);
            }
            else if (value is Hashtable)
            {
                SerializeObject((Hashtable)value, builder);
            }
            else if (value is ArrayList)
            {
                SerializeArray((ArrayList)value, builder);
            }
            else if (IsNumeric(value))
            {
                SerializeNumber(Convert.ToDouble(value), builder);
            }
            else if ((value is bool) && ((bool)value))
            {
                builder.Append("true");
            }
            else if ((value is bool) && !((bool)value))
            {
                builder.Append("false");
            }
            else
            {
                return false;
            }
            return true;
        }

        public class JSONParseErrors
        {
            [CompilerGenerated]
            private int _iErrorIndex;
            [CompilerGenerated]
            private string _sWarningText;

            public int iErrorIndex
            {
                [CompilerGenerated]
                get
                {
                    return this._iErrorIndex;
                }
                [CompilerGenerated]
                set
                {
                    this._iErrorIndex = value;
                }
            }

            public string sWarningText
            {
                [CompilerGenerated]
                get
                {
                    return this._sWarningText;
                }
                [CompilerGenerated]
                set
                {
                    this._sWarningText = value;
                }
            }
        }

        internal enum JSONTokens : byte
        {
            COLON = 5,
            COMMA = 6,
            CURLY_CLOSE = 2,
            CURLY_OPEN = 1,
            FALSE = 10,
            IMPLIED_IDENTIFIER_NAME = 12,
            NONE = 0,
            NULL = 11,
            NUMBER = 8,
            SQUARED_CLOSE = 4,
            SQUARED_OPEN = 3,
            STRING = 7,
            TRUE = 9
        }
    }
}

